/*
Brick And Blast, a crossplatform breakout game.
 Copyright (C) 2008-2010 Julien CLEMENT (andematunum@gmail.com)
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

#ifndef _GameObject_h_
#define _GameObject_h_

#include "SDL.h"
#include "ObjectGrid.h"

#include <set>
#include <map>
using namespace std;

/**
* file   GameObject.h
* date   Ven 25 déc 2009 16:14:17 CET
* brief  A GameObject is basically
         either a rectangle or a circle,
         moving or not, associated with
         a texture. GameObjects can collide
         between them.
*/

/** Defines the shape seen by the collision engine. */
enum GameObjectShape {
    GAME_OBJECT_RECTANGLE,
    GAME_OBJECT_CIRCLE
};
/** Defines the size of the object depending on its shape:
    (w,h) for a rectangular shape, (r) for a circle. */
union GameObjectSize {
  struct _RectSize_ {
    int w;
    int h;
  } rect;
  int r;
};

// Forward declaration
struct ColliderData;

/** Defines the object type, used for downcasting.
  * Don't forget to append new object types when
  * defining new sublasses. */
enum GameObjectType {
  GAME_OBJECT_BALL,
  GAME_OBJECT_BAT_CHUNK,
  GAME_OBJECT_BAT_BORDER_LEFT,
  GAME_OBJECT_BAT_BORDER_RIGHT,
  GAME_OBJECT_BUMPER,
  GAME_OBJECT_BRICK,
  GAME_OBJECT_EDGE,
  GAME_OBJECT_LASER,
  GAME_OBJECT_TARGET,
};
/** Top-level class for any game object. */
class GameObject {
public:
  GameObject ()
    : pixel_position_changed_(true), alive_(true), active_(true),
      fadeout_(255) {
  }
  /** Build a new GameObject with an initial position
    * in the pixel space, a shape and a texture.
    *
    * \param initPosition Initial (x,y) position in the
    *                     pixel space. The hot point differs
    *                     for each shape: circle's (x,y)
    *                     corresponds to the center while
    *                     rectangles's (x,y) corresponds to the
    *                     top-left corner.
    * \param shape Shape of the object as seen by the
    *              collision engine.
    * \param initTexture Initial texture identifier representing
    *                    the object on the screen.
    */                    
  GameObject(GameObjectType type,
             GameObjectShape shape,
             SDL_Point& initPosition,
             SDL_TextureID initTexture);
  /** Change the texture of this object.
      Be careful to provide a texture which matches
      the object shape. For instance, an even sized
      texture is expected for a circle.
    
      \param recompute_size Set to true to update the size, when the
                            texture size changed or when setting up
                            the initial texture (done by the constructor). */
  void set_texture (SDL_TextureID texid, bool recompute_size = true);
  SDL_TextureID get_texture () {
    return texture_;
  }
  /** Place the object at the specified (x,y) position and blit it on the screen.
    * \sa update_pixel_rect
    */
  virtual void set_position (SDL_Point& xy);
  /** Set the object shape */
  void set_shape (GameObjectShape shape) {
    shape_ = shape;
  }
  /** Set the object type */
  void set_type (GameObjectType type) {
    type_ = type;
  }
  /** Get the current rectangle around the object. */
  const SDL_Rect* get_rect () const {
    return &rect_;
  }
  /** Get the next rectangle around the object. */
  const SDL_Rect* get_next_rect () const {
    return &rect_next_;
  }
  /** Return the type of the object */
  GameObjectType get_type () {
    return type_;
  }
  /** Calculate the next pixel position of the object.
    * Depends on more specific objects.
    * Don't forget to set the \sa pixel_position_changed_
    * flag accordingly and to call \sa calculate_next_pixel_rect
    * at the end (only if the position changed).
    *
    * \sa pixel_position_changed
    * \sa calculate_next_pixel_rect
    */
  virtual void calculate_next_pixel_position () {
  }
  /** Update the pixel position.
    * \sa set_position
    */
  void update_pixel_position ();
  /** Check whether the object position changed in the
    * pixel space. */
  bool pixel_position_changed () {
    return pixel_position_changed_;
  }
  
  /** Handle the collision at the shape level and store the parameters of the intersection
    * in \p data, if any. */
  bool check_shape_collision (GameObject* obj, ColliderData* data) const;
  /** Handle the collision between an object and the game area boundaries. */
  virtual bool handle_game_area_collisions () {
    return false;
  }
  /** Handle the collision between an object and another particular object,
    * using the intersection parameter \p data.
    * This function is called only when it is known that the current object
    * collides with \p obj.
    *
    * \sa Collider
    */
  virtual void handle_collision(GameObject* obj, const ColliderData& data) {
  }
  
  /** Get the list of cells the object belongs to, 
    * for external modification.
    * \sa GridObject
    */
  set< Cell* >& get_grid_cells () {
    return grid_cells_;
  }
  /** Update the object state.
    * \return true when the state changed. */
  virtual bool update () {
    if (!active()) {
      if (fadeout_ > 0) {
        fadeout_ -= 8;
        if (fadeout_ < 0) {
          fadeout_ = 0;
        }
      }
      else {
        die();
      }
      return true;
    }
    else {
      return false;
    }
  }
  /** Blit the object on the display */
  void blit () {
    if (!active()) {
      SDL_SetTextureColorMod(texture_, fadeout_, 0, fadeout_);
      SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_ADD);
    }
    SDL_RenderCopy(texture_,NULL,&rect_);
    if (!active()) {
      SDL_SetTextureBlendMode(texture_, SDL_BLENDMODE_NONE);
    }
  }
  /** Set the object active flag. */
  void set_active (bool state) {
    active_ = state;
  }
  /** Set this object as destroyed. */
  void die () {
    alive_ = false;
  }
  /** Check if the object is active, for
    * collision tests. */
  bool active () {
    return active_;
  }
  /** Check if the object is alive. */
  bool alive () {
    return alive_;
  }
protected:
  /** Calculate the object size according to its shape.
    Internally calls \sa compute_rectangle to update
    the display rectangle. */
  void compute_size ();
  /** Update the pixel rectangle. */
  void update_pixel_rect ();
  /** Get the next pixel position of the object. */
  SDL_Point* get_next_pixel_position () {
    return &pixel_position_next_;
  }
  void calculate_next_pixel_rect ();

protected:
    /** Object collision shape: it must not necessarily
      match the real texture shape. This information
      is used to define the kind of collision for
      this object. */
  GameObjectShape shape_;
  /** Object texture: current graphics representing
      the object on the screen. It can change but is
      never animated unless a subclass defines a proper
      animation mechanism storing the current frame in
      this member variable, through the update() method. */
  SDL_TextureID texture_;
  /** Object size: it depends on the current shape.
      For a circle it is only its radius, for a rectangle
      there is a width and height.
      This variable is deduced from the current texture id. */
  GameObjectSize size_;
  /** Object type */
  GameObjectType type_;
  /** Object current pixel position */
  SDL_Point pixel_position_;
  /** Object next pixel position */
  SDL_Point pixel_position_next_;
  /** Object rectangle: current display rectangle. */
  SDL_Rect rect_;
  /** Object rectangle: next display rectangle, used
    * for collisions. */
  SDL_Rect rect_next_;
  /** State flag for pixel position changes. */
  bool pixel_position_changed_;
  /** Set of cells the object belongs to. Up to 4 cells.
    * \sa ObjectGrid
    */
  set< Cell* > grid_cells_;
  /** Active flag: set to false will ignore this object
    * in collisions. */
  bool active_;
  /** Alive flag: set to false will destroy the current
    * object at the next update of the level. */
  bool alive_;
  /** Destruction effect value. */
  int fadeout_;
};

#endif

